﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using PS3Helpers.Helpers;
using PS3Helpers.Properties;

namespace PS3Helpers
{
    public class PS3PackageCreator : IDisposable
    {
        private readonly PS3PackageFile _packageFile;
        private readonly string _selfFilename;
        private readonly OutputLogInterface _logInterface;

        public PS3PackageCreator(OutputLogInterface loginterface, PS3PackageFile packagefile, string targetpackagefilename, string signedelffilename)
        {
            _logInterface = loginterface;
            _packageFile = packagefile;
            _selfFilename = signedelffilename;

            if (string.IsNullOrEmpty(_packageFile.Filename))
                throw new Exception("Can't generate package before package file has been saved."); // ... to the folder containing the MakeFile file

            if (string.IsNullOrEmpty(signedelffilename))
                throw new Exception("Can't generate package. No signed elf specified.");

            
            string tempfolderdir = Path.GetTempPath() + Guid.NewGuid().ToString("B") + Path.DirectorySeparatorChar;

            Directory.CreateDirectory(tempfolderdir);
            try
            {
                GenerateFolderStructure(tempfolderdir);

                string pkgfilename = GeneratePackage(tempfolderdir);

                if (File.Exists(targetpackagefilename))
                    File.Delete(targetpackagefilename);

                File.Move(pkgfilename, targetpackagefilename);
            }
            finally
            {
                Directory.Delete(tempfolderdir, true);
            }
        }

        #region Dispose Implementation

        ~PS3PackageCreator()
        {
            Dispose(false);
        }

        bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    // Dispose managed resources.
                }

                // There are no unmanaged resources to release, but
                // if we add them, they need to be released here.
            }

            disposed = true;

            // If it is available, make the call to the
            // base class's Dispose(Boolean) method
            // base.Dispose(disposing);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion Dispose Implementation

        #region Folder Generation

        private void CreateChildFolders(string parentfolder, VirtualDirectory parentdir)
        {
            foreach (VirtualDirectory subdir in parentdir.Directories)
            {
                string newdir = parentfolder + subdir.Name + Path.DirectorySeparatorChar;
                Directory.CreateDirectory(newdir);

                CreateChildFolders(newdir, subdir);
            }

            foreach (VirtualFile file in parentdir.Files)
            {
                string srcfilename = file.SourceFilename;
                string targetfilename = parentfolder + file.Name;

                if (file.Locked)
                {
                    switch (file.Name)
                    {
                        case "EBOOT.BIN":
                            {
                                srcfilename = _selfFilename;
                                break;
                            }
                        case "PARAM.SFO":
                            {
                                GenerateSFO(targetfilename);
                                break;
                            }
                        case "ICON0.PNG":
                            {
                                srcfilename = _packageFile.LogoFilename;
                                if ((string.IsNullOrEmpty(srcfilename)) || (!File.Exists(srcfilename)))
                                {
                                    Resources.DefaultPS3LogoIcon.Save(targetfilename);
                                    continue;
                                }

                                break;
                            }
                    }
                }

                if (string.IsNullOrEmpty(srcfilename))
                {
                    continue;
                }

                File.Copy(srcfilename, targetfilename);
            }
        }

        private void GenerateFolderStructure(string tempfolderdir)
        {
            _logInterface.SetCurrentStep("Generating folder structure");
            CreateChildFolders(tempfolderdir, _packageFile.RootDir);
        }

        #endregion Folder Generation

        #region Package generation

        private void GenerateSFO(string filename)
        {
            _logInterface.SetCurrentStep("Generating SFO file");

            PS3SFO sfo = new PS3SFO(true);
            sfo.Bootable = true;
            sfo.Title = _packageFile.Title;
            sfo.Category = _packageFile.Category;
            sfo.TitleId = _packageFile.ContentId;
            // sfo.ApplicationVersion = _packageFile.AppVer;
            sfo.Version = "01.01";

            sfo.ParentalLevel = _packageFile.ParentalLockLevel;


            sfo.VideoResolution = _packageFile.VideoResolution;
            sfo.AudioFormat = _packageFile.AudioFormat;
            sfo.PS3SystemVersion = _packageFile.Ps3SystemVersion;
            sfo.ApplicationVersion = _packageFile.AppVer;

            sfo.SaveFile(filename);
        }

        private void GeneratePackageConfigurationFile(string tempfolderdir)
        {
            _logInterface.SetCurrentStep("Generating package configuration file");

            using (FileStream confstream = File.Create(tempfolderdir + "package.conf"))
            {
                using (StreamWriter sw = new StreamWriter(confstream, Encoding.ASCII))
                {
                    sw.WriteLine("Content_ID = " + _packageFile.ContentId);
                    sw.WriteLine("K_licensee = " + _packageFile.KLicense);

                    switch (_packageFile.DrmType)
                    {
                        case PS3DrmType.Free:
                            {
                                sw.WriteLine("DRMType = Free");
                                break;
                            }
                        case PS3DrmType.Local:
                            {
                                sw.WriteLine("DRMType = Local");
                                break;
                            }
                        default:
                            {
                                throw new Exception("Unknown PS3 DRM Type!");
                            }
                    }

                    // TODO! FIX THIS ONE!
                    switch (_packageFile.ContentType)
                    {
                        case PS3ContentType.GameData:
                            {
                                // sw.WriteLine("ContentType = GameData");
                                sw.WriteLine("ContentType = GameExec");
                                break;
                            }
                        case PS3ContentType.GameExec:
                            {
                                sw.WriteLine("ContentType = GameExec");
                                break;
                            }
                        default:
                            {
                                throw new Exception("Unknown PS3 Content Type!");
                            }
                    }

                    sw.WriteLine("PackageVersion = 01.00"); // 02.00
                    /*
                    # This Content ID is only for sample. Please modify to what you owned.
                    Content_ID = IV0002-NPXS00008_00-GAMEUPDATEPATCH1

                    # This k_licensee is only for sample. Please modify to what you want.
                    # Please use a unique value for each title except if you have special reason.
                    # ( unique values for each packages are especially recommended. )
                    K_licensee = 0xc0a3b3641c2ad1ef23153a48a3e12fe7

                    # Please select DRM type.
                    # Local: the package will be distributed with charge.
                    # Free : the package will be distributed without charge.
                    DRMType = Free

                    # Please select content type.
                    # GameData: the package will appear in the Game Data Utility.
                    # GameExec: the package will appear in the Game Column and is executable.
                    ContentType = GameData

                    # Please set a master package version for submission.
                    PackageVersion = 01.00
                     */
                }
            }
        }

        private string GeneratePackage(string tempfolderdir)
        {
            GeneratePackageConfigurationFile(tempfolderdir);


            _logInterface.SetCurrentStep("Making package");

            ProcessExecutor makepackage = new ProcessExecutor(
                                                              _logInterface,
                                                              tempfolderdir,
                                                              @"make_package_npdrm.exe",
                                                              // string.Format("-v -f {0} ./{1}", packageconffilename, _temporaryPackageDirectoryName)
                                                              "-v",
                                                              true
                                                             );

            string pkgfilename = tempfolderdir + _packageFile.ContentId + ".pkg";
            if (!File.Exists(pkgfilename))
                throw new Exception("Package file generation failed! Pkg not found. " + pkgfilename);

            return pkgfilename;
        }

        #endregion Package generation
    }
}
